use ruma::{events::RoomAccountDataEventType, RoomId, UserId};
use serde_json::value::RawValue;

use crate::{Error, Result, DATABASE};

const ROOMUSERTYPE_ROOMUSERDATAID_DEFINATION: redb::TableDefinition<&[u8], Vec<u8>> =
    redb::TableDefinition::new("roomusertype_roomuserdataid");

const ROOMUSERDATAID_ACCOUNTDATA_DEFINATION: redb::TableDefinition<&[u8], Vec<u8>> =
    redb::TableDefinition::new("roomuserdataid_accountdata");

pub struct Service;

impl Service {
    pub fn new() -> Self {
        let write_ctx = DATABASE.begin_write().unwrap();
        write_ctx
            .open_table(ROOMUSERTYPE_ROOMUSERDATAID_DEFINATION)
            .unwrap();
        write_ctx
            .open_table(ROOMUSERDATAID_ACCOUNTDATA_DEFINATION)
            .unwrap();
        write_ctx.commit().unwrap();

        Self
    }

    pub fn get(
        &self,
        room_id: Option<&RoomId>,
        user_id: &UserId,
        event_type: &RoomAccountDataEventType,
    ) -> Result<Option<Box<RawValue>>> {
        let mut key = room_id
            .map(ToString::to_string)
            .unwrap_or_default()
            .as_bytes()
            .to_vec();
        key.push(0xFF);
        key.extend_from_slice(user_id.as_bytes());
        key.push(0xFF);
        key.extend_from_slice(event_type.to_string().as_bytes());

        let read_ctx = &DATABASE.begin_read().map_err(redb::Error::from)?;
        let roomusertype_roomuserdataid_table = read_ctx
            .open_table(ROOMUSERTYPE_ROOMUSERDATAID_DEFINATION)
            .map_err(redb::Error::from)?;
        roomusertype_roomuserdataid_table
            .get(key.as_slice())
            .map_err(redb::Error::from)?
            .and_then(|roomuserdataid| {
                read_ctx
                    .open_table(ROOMUSERDATAID_ACCOUNTDATA_DEFINATION)
                    .unwrap()
                    .get(roomuserdataid.value().as_slice())
                    .transpose()
            })
            .transpose()
            .map_err(redb::Error::from)?
            .map(|data| {
                serde_json::from_slice(data.value().as_slice())
                    .map_err(|_| Error::BadDatabase("could not deserialize"))
            })
            .transpose()
    }
}
